package com.moyan.calculator;

import android.util.Log;

import java.text.DecimalFormat;
import java.util.StringTokenizer;

public class Calculate {
    //设定最大的运算符长度
    private final int MAXLEN = 500;

    /**
     * 计算表达式的字符串形式
     * 从左到右扫面，数字先入number栈，运算符入operator栈
     * +-基本优先级为1 *÷的优先级为2，log ln sin cos tan n!的优先级为3，开方，乘方的优先级为4
     * 括号内存运算法的优先级高于4
     * 当前运算符优先级高于栈顶压栈，低于栈顶弹出一个运算符与两个数字进行运算
     * 重复直到运算符大于栈顶
     * 扫面完之后对剩下的运算符与数字依次进行计算
     *
     * @param str
     */
    public  String process(String str, boolean drag_flag) {
        int weightPlus = 0;  //记录同一个()的基本优先级
        int topTop = 0; //数组计数器
        int topNum = 0;
          //判断正负 1为正数 -1为负数
        int weightTemp = 0; //记录临时优先级的变化
        int weight[] = new int[MAXLEN];
        double[] number = new double[MAXLEN];
        char ch, ch_gai, operator[] = new char[MAXLEN];
        String num; // 记录数字 str将以+-×÷()sctgl!√ ^分割，字符串之间即为数字
        String experssion = str;
        StringTokenizer expToken = new StringTokenizer(experssion, "+-×÷()sabcdtglme!√ ^");
        int i = 0;
        int flag = 1;
        while (i < experssion.length()) {
            ch = experssion.charAt(i);
            //判断正负数
            if (i == 0) {
                if (ch == '-') {
                    flag = -1;
                    topNum++;
                }
            }else if(i==1){
                if(ch=='-'){
                    topNum++;
                }
            } else if (i>1&&experssion.charAt(i - 1) == '(' && ch == '-'){
//                topNum++;
                flag = -1;
            }

            //取得数字，将正负号交给该数字
            if (ch >= '0' && ch <= '9' || ch == '.' || ch == 'E') {
                num = expToken.nextToken();
                ch_gai = ch;
                while (i < experssion.length() && (ch_gai >= '0' && ch_gai <= '9' || ch_gai == '.' || ch_gai == 'E')) {
                    ch_gai = experssion.charAt(i++);
                    System.out.println("i的值为：" + i + " , ch_gai的值为：" + ch_gai);
                }

                if (i >= experssion.length()) i -= 1;
                else i -= 2;

                if (num.compareTo(".") == 0) number[topNum++] = 0;
                else {
                    number[topNum++] = Double.parseDouble(num) * flag;
                    flag = 1;
                }
            }

            //计算运算符的优先级
            if (ch == '(') weightPlus += 4;
            if (ch == ')') weightPlus -= 4;
            if (ch == '-' && flag == 1 || ch == '+' || ch == '-' || ch == '×' || ch == '÷' ||
                    ch == 's' || ch == 'c' || ch == 't' || ch == 'l' || ch == 'g' || ch == '!' ||
                    ch == '√' || ch == '^'||ch=='a'||ch=='b'||ch=='d'||ch=='m'||ch=='e') {
                switch (ch) {
                    //+-的优先级最低
                    case '+':
                    case '-':
                        weightTemp = weightPlus + 1;
                        break;
                    //×÷的优先级为2
                    case '÷':
                    case 'm':
                    case '×':
                        weightTemp = weightPlus + 2;
                        break;
                    case 's':
                    case 'c':
                    case 't':
                    case 'l':
                    case 'a':
                    case 'b':
                    case 'd':
                    case 'g':
                    case '!':
                    case 'e':
                        weightTemp = weightPlus + 3;
                        break;
                    case '√':
                    case '^':
                    default:
                        weightTemp = weightPlus + 4;
                        break;
                }

                //如果当前优先级大于栈顶的元素，则直接入栈
                if (topTop == 0 || weight[topTop - 1] < weightTemp&& flag==1) {
                    weight[topTop] = weightTemp;
                    operator[topTop] = ch;
                    System.out.println("============" + ch + "========");
                    topTop++;
                } else { //否则将堆栈中的运算符逐个取出，直到当前栈顶部运算符的优先级小于当前运算符
                    while (topTop > 0 && weight[topTop - 1] > weightTemp) {
                        switch (operator[topTop - 1]) {
                            case '+':
                                number[topNum - 2] += number[topNum - 1];
                                break;
                            case '-':
                                number[topNum - 2] -= number[topNum - 1];
                                break;
                            case '×':
                                number[topNum - 2] *= number[topNum - 1];
                                break;
                            case '÷':
                                if (number[topNum - 1] == 0) {
                                    System.out.println("0不能为除数");
                                    return null;
                                }
                                number[topNum - 2] /= number[topNum - 1];
                                break;
                            case 'm':
                                if (number[topNum - 1] == 0) {
                                    System.out.println("0不能为除数");
                                    return null;
                                }
                                number[topNum - 2] %= number[topNum - 1];
                                break;
                            case '√':
                                //负值不能开偶次方
                                if (number[topNum - 1] == 0 || (number[topNum - 2] < 0 && number[topNum - 1] % 2 == 0)) {
                                    System.out.println("平方数不存在或者负数不能开偶次行！");
                                    return null;
                                }
                                number[topNum - 2] = Math.pow(number[topNum - 1], 1 / number[topNum - 2]);
                                break;
                            case '^':
                                number[topNum - 2] = Math.pow(number[topNum - 2], number[topNum - 1]);
                                break;
                            case 's':
                                //角度
                                if (drag_flag) {
                                    number[topNum - 1] = Math.sin(number[topNum - 1] * Math.PI / 180);
                                } else {
                                    //此时为弧度
                                    number[topNum - 1] = Math.sin(number[topNum - 1]);
                                }
                                topNum++;
                                break;
                            case 'c':
                                if (drag_flag) {
                                    number[topNum - 1] = Math.cos(number[topNum - 1] * Math.PI / 180);
                                } else {
                                    number[topNum - 1] = Math.cos(number[topNum - 1]);
                                }
                                topNum++;
                                break;
                            case 't':
                                if (drag_flag) {
                                    if (Math.abs(number[topNum - 1] % 90) == 1) {
                                        System.out.println("tan取值有错误");
                                        return null;
                                    }
                                    number[topNum - 1] = Math.tan(number[topNum - 1] * Math.PI / 180);
                                } else {
                                    if (Math.abs(number[topNum - 1] % Math.PI * 2) == 1) {
                                        System.out.println("tan取值有错误");
                                        return null;
                                    }
                                    number[topNum - 1] = Math.tan(number[topNum - 1]);
                                }
                                topNum++;
                                break;
                            //ln
                            case 'a':

                                if (drag_flag) {
                                    number[topNum - 1] =Math.toDegrees(Math.asin(number[topNum - 1])) ;
                                } else {
                                    //此时为弧度
                                    number[topNum-1]=Math.asin(number[topNum-1]);
                                }

                                topNum++;
                                break;
                            case 'b':
                                if (drag_flag) {
                                    number[topNum - 1] =Math.toDegrees(Math.acos(number[topNum - 1])) ;
                                } else {
                                    //此时为弧度
                                    number[topNum-1]=Math.acos(number[topNum-1]);
                                }
                                topNum++;
                                break;
                            case 'd':
                                if (drag_flag) {
                                    number[topNum - 1] =Math.toDegrees(Math.atan(number[topNum - 1]));
                                } else {
                                    //此时为弧度
                                    number[topNum-1]=Math.atan(number[topNum-1]);
                                }
                                topNum++;
                                break;
                            case 'l':
                                if (number[topNum - 1] <= 0) {
                                    System.out.println("对数ln值有误");
                                    return null;
                                }
                                number[topNum - 1] = Math.log(number[topNum - 1]);
                                topNum++;
                                break;
                            //log
                            case 'g':
                                if (number[topNum - 1] <= 0) {
                                    System.out.println("对数log值有误");
                                    return null;
                                }
                                number[topNum - 1] = Math.log(number[topNum - 1]);
                                topNum++;
                                break;
                            case 'e':
                                if (number[topNum - 1] <= 0) {
                                    System.out.println("对数log值有误");
                                    return null;
                                }
                                number[topNum - 1] = Math.log10(number[topNum - 1]);
                                topNum++;
                                break;
                            case '!':
                                if (number[topNum - 1] < 0) {
                                    System.out.println("阶乘数有误");
                                    return null;
                                }
                                number[topNum - 1] = getN(number[topNum - 1]);
                                topNum++;
                                break;
                        }
                        //继续堆栈下一个元素判断
                        topNum--;
                        topTop--;
                    }
                    //将运算符压入栈中
                    if(flag==1){
                        weight[topTop] = weightTemp;
                        operator[topTop] = ch;
                        topTop++;
                    }
                }
            }
            i++;
        }

        //依次取出堆运算中的运算符进行运算
        while (topTop > 0) {
            //直接将数组的后两位取出运算
            switch (operator[topTop - 1]) {
                case '+':
                    number[topNum - 2] += number[topNum - 1];
                    break;
                case '-':
                    number[topNum - 2] -= number[topNum - 1];
                    break;
                case '×':
                    number[topNum - 2] *= number[topNum - 1];
                    break;
                case '÷':
                    if (number[topNum - 1] == 0) {
                        System.out.println("0不能为除数");
                        return null;
                    }
                    number[topNum - 2] /= number[topNum - 1];
                    break;
                case 'm':
                    if (number[topNum - 1] == 0) {
                        System.out.println("0不能为除数");
                        return null;
                    }
                    number[topNum - 2] %= number[topNum - 1];
                    break;
                case '√':
                    //负值不能开偶次方
                    if (number[topNum - 1] == 0 || (number[topNum - 2] < 0 && number[topNum - 1] % 2 == 0)) {
                        System.out.println("平方数不存在或者负数不能开偶次方！");
                        return null;
                    }
                    number[topNum - 2] = Math.pow(number[topNum - 1], 1 / number[topNum - 2]);
                    break;
                case '^':
                    number[topNum - 2] = Math.pow(number[topNum - 2], number[topNum - 1]);
                    break;
                case 's':
                    //角度
                    if (drag_flag) {
                        number[topNum - 1] = Math.sin(number[topNum - 1] * Math.PI / 180);
                    } else {
                        //此时为弧度
                        number[topNum - 1] = Math.sin(number[topNum - 1]);
                    }
                    topNum++;
                    break;
                case 'a':
                    if (drag_flag) {
                        number[topNum - 1] =Math.toDegrees(Math.asin(number[topNum - 1]));
                    } else {
                        //此时为弧度
                        number[topNum-1]=Math.asin(number[topNum-1]);
                    }
                    topNum++;
                    break;
                case 'b':
                    if (drag_flag) {
                        number[topNum - 1] =Math.toDegrees(Math.acos(number[topNum - 1]));
                    } else {
                        //此时为弧度
                        number[topNum-1]=Math.acos(number[topNum-1]);
                    }
                    topNum++;
                    break;
                case 'd':
                    if (drag_flag) {
                        number[topNum - 1] =Math.toDegrees(Math.atan(number[topNum - 1]));
                    } else {
                        //此时为弧度
                        number[topNum-1]=Math.atan(number[topNum-1]);
                    }
                    topNum++;
                    break;
                case 'c':
                    if (drag_flag) {
                        number[topNum - 1] = Math.cos(number[topNum - 1] * Math.PI / 180);
                    } else {
                        number[topNum - 1] = Math.cos(number[topNum - 1]);
                    }
                    topNum++;
                    break;
                case 't':
                    if (drag_flag) {
                        if (Math.abs(number[topNum - 1] % 90) == 1) {
                            System.out.println("tan取值有错误");
                            return null;
                        }
                        number[topNum - 1] = Math.tan(number[topNum - 1] * Math.PI / 180);
                    } else {
                        if (Math.abs(number[topNum - 1] % Math.PI * 2) == 1) {
                            System.out.println("tan取值有错误");
                            return null ;
                        }
                        number[topNum - 1] = Math.tan(number[topNum - 1]);
                    }
                    topNum++;
                    break;
                //ln
                case 'l':
                    if (number[topNum - 1] <= 0) {
                        System.out.println("对数ln值有误");
                        return null;
                    }
                    number[topNum - 1] = Math.log(number[topNum - 1]);
                    topNum++;
                    break;
                //log
                case 'g':
                    if (number[topNum - 1] < 0) {
                        System.out.println("对数log值有误");
                        return null;
                    }
                    number[topNum - 1] = Math.log10(number[topNum - 1]);
                    topNum++;
                    break;
                case 'e':
                    if (number[topNum - 1] <= 0) {
                        System.out.println("对数log值有误");
                        return null;
                    }
                    number[topNum - 1] = Math.log10(number[topNum - 1]);
                    topNum++;
                    break;
                case '!':
                    if (number[topNum - 1] < 0) {
                        System.out.println("阶乘数有误");
                        return null;
                    }
                    number[topNum - 1] = getN(number[topNum - 1]);
                    topNum++;
                    break;
            }
            //取下一个元素计算
            topNum--;
            topTop--;
        }

        if (number[0] > 7.3E306) {
            System.out.println("计算数组过大");
            return null;
        }

        //数据显示:
//        System.out.println(dataShow(number[0]));
        Log.d("最终计算答案为：" , dataShow(number[0]));
        return dataShow(number[0]);
    }

    private double getN(double n) {
        if (n == 0)
            return 1;
        double sum = 1.0;
        for (int s = 1; s <= n; s++) {
            sum *= s;
        }
        return sum;
    }

    private String dataShow(double n) {
        DecimalFormat format = new DecimalFormat("0.############");
        return format.format(n);
    }

//    public static void main(String[] args) {
//        Calculate ct =new Calculate();
//        ct.process("12×12", true);
//
//    }

}//log就用g代替，ln就用l，sin用s代替，cos用o代替，tan用t代替